/*
 * Copyright (C) 2009 Leandro Aparecido <lehphyro@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.metalframework.persistence.engine;

import java.lang.reflect.*;
import java.util.*;

/**
 * The Engine is responsible for managing all persistence operations.
 * <p>This interface is not intended to be used directly. Please see
 * {@link Repository} if you want to execute custom queries or perform other
 * operations.
 * <p>If you are a framework developer, you might be interested in {@link org.metalframework.persistence.interceptor.JapaAspectSupport}
 * support class to implement annotation based interceptors. {@link org.metalframework.persistence.interceptor.autopersist.AutomaticPersistenceInterceptor}
 * is a good example of AOP interceptor that uses JAPA under the covers.
 * 
 * @since 1.0
 * @see org.metalframework.persistence.annotation.RepositoryQuery
 * @see org.metalframework.persistence.annotation.RepositoryReturn
 * @see org.metalframework.persistence.interceptor.autopersist.AutomaticPersistenceInterceptor
 */
public interface Engine {
	/**
	 * Reference to current Japa Engine. Useful when around intercepting method invocations.
	 */
	ThreadLocal<Engine> LOCAL_JAPA_ENGINE = new ThreadLocal<Engine>();
	
	/**
	 * Every object that needs declarative persistence must be preprocessed by JAPA to have extents
	 * and other functions injected/available.
	 * <p>Note that you should call this method only <b>once for each managed object</b>.
	 * Subsequent calls will reprocess the object destroying all non-flushed persistent state.
	 * 
	 * @param obj Object annotated with Japa annotations to be processed.
	 * @return Japa managed object required to perform other operations.
	 */
	PersistentObject processObject(Object obj);

	/**
	 * Must be called before method invocations to process method arguments against the JPA repository.
	 * 
	 * @param obj JapaObject returned by {@link #processObject(Object)}.
	 * @param method Method being invoked.
	 * @param args Arguments of method invocation.
	 * @return Result of the method invocation processing.
	 */
	MethodCallContext processMethodCall(PersistentObject obj, Method method, Object... args);

	/**
	 * Must be called after method invocations to process method return against the JPA repository.
	 * 
	 * @param context Method invocation context as returned by {@link #processMethodCall(PersistentObject, Method, Object...)}.
	 * @param returnValue Return value of the method invocation.
	 * @return New return value of the method invocation.
	 * @throws Throwable If some error ocurred while interacting with JPA or JAPA was configured
	 * to throw an exception during its processing.
	 */
	Object processMethodReturn(MethodCallContext context, Object returnValue) throws Throwable;

	/**
	 * Executes a custom JPA query against the JPA repository.
	 * 
	 * @param <T> Entity type of the result.
	 * @param clause JPA custom query to execute.
	 * @param params JPA query positional parameters.
	 * @return List of entities returned by the JPA engine.
	 */
	<T> List<T> query(String clause, Object... params);

	/**
	 * Fetches an instance from the JPA repository.
	 * 
	 * @param <T> Entity type of the result.
	 * @param entityClass Entity class.
	 * @param id ID of the entity instance to retrieve.
	 * @return Entity or null if not found.
	 */
	<T> T findById(Class<T> entityClass, Object id);

	/**
	 * Refreshs the entity state.
	 * 
	 * @param <T> Entity type.
	 * @param entity Entity to refresh.
	 */
	<T> void refresh(T entity);
	
	/**
	 * Persists the entity in the JPA repository.
	 * 
	 * @param <K> Primary key type of the entity.
	 * @param <T> Entity type.
	 * @param entityClass Entity class.
	 * @param entity Entity to persist.
	 * @return Primary key value under which the entity has been persisted.
	 */
	<K, T> K persist(Class<T> entityClass, T entity);
	
	/**
	 * Updates the entity data in the JPA repository.
	 * 
	 * @param <T> Entity type.
	 * @param entity Entity to merge.
	 */
	<T> void merge(T entity);
	
	/**
	 * Updates the entity data in the JPA repository.
	 * 
	 * @param <T> Entity type.
	 * @param entityClass Entity class.
	 * @param id Primary key of the entity to be merged.
	 * @param entity Entity to merge.
	 */
	<T> void merge(Class<T> entityClass, Object id, T entity);

	/**
	 * Removes the entity data from the JPA repository.
	 * 
	 * @param <T> Entity type.
	 * @param entity Entity to remove.
	 */
	<T> void remove(T entity);

	/**
	 * Executes a Jpa Callback.
	 * 
	 * @param <T> Result type.
	 * @param callback Callback to execute.
	 * @return Return value of the callback.
	 */
	<T> List<T> execute(Callback callback);
}